package copyengine.ui.component.list.old
{
	import copyengine.ui.CESprite;
	import copyengine.ui.component.list.old.animation.ICEListAnimation;
	import copyengine.ui.component.list.old.cellrender.ICEOldCellRender;
	import copyengine.ui.component.list.old.cellrender.ICECellRenderData;
	import copyengine.ui.component.list.old.dataprovider.ICEListDataProvider;
	import copyengine.utils.GeneralUtils;

	/**
	 * CEListCore is List component root , you should not use this class directly.
	 * this class should use combination with other CEComponent (ICEListAnimation ,Button ,ScrollBar etc).
	 *
	 * CEList only provide baise get/set scrollPosition , get/set selected
	 * other useful function (pageDown,pageUp etc )should provide by child class or the class combination with CEListCore.
	 *
	 * use this class need to
	 * 					1) set an dataProvide					(the list data source)
	 * 					2) set layoutDirection 					(either horizontal or vertical)
	 * 					3) set displayCount    					(each page display CellRender number)
	 * 					4) set cellRender width/height		(the visable cellRender widht/height should all the same)
	 * 					5) set contentPadding					(the distance bewteen each cellRender)
	 * 					6) set cellRenderInstanceClass      (the cellRender class, this clas should implements ICECellRender)
	 *
	 * @author Tunied
	 *
	 */
	public class CEListCore extends CESprite
	{
		public static const LAYOUT_HORIZONTAL:String = "h"; //horizontal
		public static const LAYOUT_VERTICAL:String = "v"; //vertical

		/**
		 * define is current list core support selected function or not. if outside logic can not deal the logic immediately when
		 * user click one cellRender , or need to wait user click more than one cellRender then deal the logic, need to change
		 * the selectable property to SELECTABLE_SINGLE_SELECTED or SELECTABLE_MULTIPLE_SELECTED;
		 */
		public static const SELECTABLE_CAN_NOT_SELECTED:int = 1;
		public static const SELECTABLE_SINGLE_SELECTED:int = 2;
		public static const SELECTABLE_MULTIPLE_SELECTED:int = 3;

		/**
		 * hold all visable CellRender , the number should be  displayCount+1;
		 *
		 * WARNINIG:
		 * 			create by initialize function no need set by user.
		 */
		protected var cellRenderVector:Vector.<ICEOldCellRender>;

		/**
		 * store all CEList data.
		 */
		protected var dataProvider:ICEListDataProvider;

		/**
		 *  each page display cellRenderGroup number
		 */
		protected var displayGroupCount:int;

		/**
		 * should be either horizontal or vertical
		 */
		protected var layoutDirection:String;


		/**
		 * each CEList cellRender should have same width and height ,and all equal with
		 * eachCellRenderWidth and eachCellRenderHeight property
		 */
		protected var eachCellRenderWidth:int;
		protected var eachCellRenderHeight:int;

		/**
		 * listInteraction use to deal will list scrolling animation.
		 */
		private var listInteraction:ICEListAnimation;

		/**
		 * define the padded for each cellRenderGroup.
		 */
		protected var groupContentPadding:int;

		/**
		 * define the max scrollPosition.
		 *
		 * WARNINIG:
		 * 			calculate by initialize function no need set by user.
		 */
		private var maxScrollPosition:int;

		/**
		 *this number should be eachCellRenderWidth/eachCellRenderHeight + contentPadding
		 * store this number is use to do the math fast.
		 *
		 * WARNINIG:
		 * 			calculate by initialize function no need set by user.
		 */
		private var cellRenderGroupDistance:int;

		private var selectableProperty:int;

		/**
		 *
		 * @param _displayCount							@see displayCount
		 * @param _cellRenderInstanceClass		use this class to initialze each visableCellRender(this class should implements ICECellRender)
		 * @param _listInteractionClass				@see listInteractionClass
		 * @param _layoutDirection						@see layoutDirection
		 * @param _dataProvider							@see dataProvider
		 * @param _eachCellRenderWidth			@see eachCellRenderWidth
		 * @param _eachCellRenderHeight			@see eachCellRenderHeight
		 * @param _contentPadding						@see contentPadding
		 *
		 */
		public function CEListCore(_displayGroupCount:int, _layoutDirection:String, _eachCellRenderWidth:int,
								   _eachCellRenderHeight:int, _contentPadding:int)
		{
			super();
			displayGroupCount = _displayGroupCount;
			layoutDirection = _layoutDirection;
			eachCellRenderWidth = _eachCellRenderWidth;
			eachCellRenderHeight = _eachCellRenderHeight;
			groupContentPadding = _contentPadding;
		}

		public final function initializeCEListCore(_cellRenderInstanceClass:Class,
												   _dataProvider:ICEListDataProvider = null,
												   _listInteraction:ICEListAnimation = null,
												   _cellRenderAnimationClass:Class = null,
												   _selectableProperty:int = SELECTABLE_CAN_NOT_SELECTED):void
		{
			selectableProperty = _selectableProperty;
			listInteraction = _listInteraction;
			_scrollPosition = 0;
			cellRenderGroupDistance = getCellRenderBoundSize() + groupContentPadding;

			setDataProvider(_dataProvider);

			if (listInteraction != null)
			{
				listInteraction.target = this;
			}

			calculateMaxScrollPosition();

			initListCoreScrollRect();

			initializeAllCellRender(_cellRenderInstanceClass, _cellRenderAnimationClass);

			layoutAllCellRender();
			refreshAllCellRenderDispaly();

			initializeCellRenderSelectableListener();
		}

		override final protected function doDispose():void
		{
			if (listInteraction != null)
			{
				listInteraction.dispose();
			}
			disposeCellRenderSelectableListener();
			disposeAllCellRender();
			disposeDataProvider();
		}

		private function initializeCellRenderSelectableListener():void
		{
			if (selectableProperty != SELECTABLE_CAN_NOT_SELECTED)
			{
				for each (var cellRender:ICEOldCellRender in cellRenderVector)
				{
					GeneralUtils.addTargetEventListener(cellRender.container, CEListCoreEvent.ITEM_CLICK, onCellRenderClick);
				}
			}
		}

		private function disposeCellRenderSelectableListener():void
		{
			for each (var cellRender:ICEOldCellRender in cellRenderVector)
			{
				GeneralUtils.removeTargetEventListener(cellRender.container, CEListCoreEvent.ITEM_CLICK, onCellRenderClick);
			}
		}

		private function onCellRenderClick(e:CEListCoreEvent):void
		{
			//如果点击的Cellrender没有Data则认为该点击无效
			if (e.clickItemData && e.clickItemData.isCanSelected())
			{
				if (selectableProperty == SELECTABLE_SINGLE_SELECTED)
				{
					dataProvider.forEach(cleanEachCellRenderDataSelectableProperty);
					cellRenderVector.forEach(cleanEachCellRenderSelectableProperty);
				}
				e.clickItemData.isSelected = !e.clickItemData.isSelected;

				var cellRender:ICEOldCellRender = getCellRenderByCellIndex(e.clickItemIndex);
				if (cellRender && e.clickItemData)
				{
					cellRender.setIsSelected(e.clickItemData.isSelected);
				}
				refreshAllCellRenderDispaly();
			}
		}

		/**
		 * refresh current CEList data provider. when do this will refresh ceList display part.
		 * and change it with new dataprovider.
		 *
		 * @param _newDataProvider		if this property is null means data provider has been change already
		 *
		 */
		public final function refreshDataProvider(_newDataProvider:ICEListDataProvider = null,
												  _isCleanScrollPositon:Boolean = false):void
		{
			// if newDataProvider = null 
			// means current dataProvider not beean chang , only change the data in the dataProvider
			if (_newDataProvider != null)
			{
				setDataProvider(_newDataProvider);
			}
			if (_isCleanScrollPositon)
			{
				cleanScrollPosition();
			}
			calculateMaxScrollPosition();
			layoutAllCellRender();
			refreshAllCellRenderDispaly();
			if (listInteraction != null)
			{
//				listInteraction.killAnimation();
			}
		}

		/**
		 *滚动到指定CellRenderIndex
		 * CellRenderIndex需要从对应的DataProvider中得到,因为Multiple的Single对应的Index算法不同
		 */
		public final function scrollToIndex(_groupIndex:int, _isPlayingAnimation:Boolean = true):void
		{
			if (_groupIndex != int.MIN_VALUE)
			{
				_groupIndex = Math.max(_groupIndex, 0);
				var position:int = Math.min((getCellRenderBoundSize() + groupContentPadding) * _groupIndex, maxScrollPosition);

				if (_isPlayingAnimation)
				{
					scrollPosition = position;
				}
				else
				{
					expectantScrollPosition = position;
					privateAnimationScrollPosition = position;
				}
			}
		}

		public final function getSelectableItems():Vector.<ICECellRenderData>
		{
			return dataProvider.filter(filterSelectableCellRenderData);
		}

		public final function getLineScrollSize():int
		{
			return cellRenderGroupDistance;
		}

		public final function getPageScrollSize():int
		{
			return cellRenderGroupDistance * displayGroupCount;
		}

		public final function getMinScrollValue():int
		{
			return 0;
		}

		public final function getMaxScrollValue():int
		{
			return maxScrollPosition;
		}

		public final function getListPageCount():int
		{
			return displayGroupCount;
		}

		public final function getListCellRenderCount():int
		{
			return displayGroupCount + 1;
		}

		//================
		//==lnteraction Function
		//================
		public final function scrollNext():void
		{
			scrollPosition = Math.min(_scrollPosition + cellRenderGroupDistance - getFirstCellRenderGroupOffset(), maxScrollPosition);
		}

		public final function scrollPrev():void
		{
			var offset:int = getFirstCellRenderGroupOffset();
			if (offset > 0)
			{
				scrollPosition = Math.max(0, _scrollPosition - offset);
			}
			else
			{
				scrollPosition = Math.max(0, _scrollPosition - cellRenderGroupDistance);
			}
		}

		public final function scrollNextPage():void
		{
			scrollPosition = Math.min(_scrollPosition + cellRenderGroupDistance * displayGroupCount - getFirstCellRenderGroupOffset(),
									  maxScrollPosition);
		}

		public final function scrollPrevPage():void
		{
			//if want scroll to prevPage
			//first assume scrollPosition just at the left-top of the cellRender no offset.
			//in that case need to scroll  cellRenderDistance*displayCount- contentPadding can get to prevPgae.
			//then if scrollPosition have the offset . means in prev calculate had sub more than real need to add that part back
			//the part is getCellRenderBoundSizeByLayout() - calculateOffsetOfFirstVisableCellRender()
			scrollPosition = Math.max(0, _scrollPosition - cellRenderGroupDistance * (displayGroupCount + 1) + groupContentPadding + getCellRenderBoundSize() - getFirstCellRenderGroupOffset());
		}

		public final function scrollEnd():void
		{
			scrollPosition = maxScrollPosition;
		}

		public final function scrollStart():void
		{
			scrollPosition = 0;
		}

		/**
		 *清除当前的ScrollPosition
		 *
		 */
		public final function cleanScrollPosition():void
		{
			listInteraction && listInteraction.killAnimation();
			privateAnimationScrollPosition = 0;
			expectantScrollPosition = 0;
		}

		//=============
		//== Scroll Position
		//=============
		/**
		 * scrollPosition is use to determine which item will showing in the screen,
		 * and where are the item are showing.
		 * we can imagine all items are arrange in horizontal/vertical orders . and the
		 * List just like an camera, and scrollPosition just define the camera position.
		 *
		 * all Scrolling function finally always call this function to move the "camera"
		 */
		private var _scrollPosition:int = 0;
		private var oldScrollPosition:int = 0;

		/**
		 * WARNING::
		 * 		get scrollPosition is use out of CEListCore Class to get the expectance scrollPosition.
		 * when trigger set scrollPosition function , will start to play list animation(if exist) so that time the reall
		 * srcroll position will not change yet
		 */
		private var expectantScrollPosition:int = 0;

		public final function get scrollPosition():int
		{
			return expectantScrollPosition;
		}

		public final function set scrollPosition(value:int):void
		{
			if (value == _scrollPosition)
			{
				return;
			}
			else
			{
				expectantScrollPosition = value;
				if (listInteraction != null)
				{
					//will play animation and scroll position will change by animation call back
					listInteraction.scrollPosition = value;
				}
				else
				{
					privateAnimationScrollPosition = value;
				}
				if (hasEventListener(CEListCoreEvent.SCROLL_START))
				{
					var event:CEListCoreEvent = new CEListCoreEvent(CEListCoreEvent.SCROLL_START);
					event.maxScrollPosition = maxScrollPosition;
					event.expectScrollPositon = value;
					event.currentScrollPosition = _scrollPosition;
					this.dispatchEvent(event);
					event = null;
				}
			}
		}

		/**
		 *	@private
		 *
		 *  this function should only call by listInteraction when the scrollPosition change,
		 *  set scrollPosition(value:Number) will ask listInteraction to change the scrollPositon.
		 *  listInteraction will releate to it implement to decide how many pixel it will change each render.
		 *  use an set function because it convenience for tweener to change the property.
		 */
		public final function set privateAnimationScrollPosition(value:int):void
		{
			oldScrollPosition = _scrollPosition;
			_scrollPosition = GeneralUtils.normalizingValue(value, 0, maxScrollPosition);
			scrollListPosition();
		}

		public final function get privateAnimationScrollPosition():int
		{
			return _scrollPosition;
		}

		private function scrollListPosition():void
		{
			adjustCellRenderDisplayByScrollPosition();
			layoutAllCellRender();
		}

		/**
		 * if the scrollDelta more than one cellRenderWidth/cellRenderHeight then
		 * we can sure that cellRender has been scroll out of the screen ,and we can reuse it.
		 *
		 * this function goal is to recycle all cellRender and reIntiailze cellRender which already
		 * out of screen. others are just keep it.
		 *
		 * WARNINIG::
		 * 		this function only adjust each cellRender display part not change the cellRedner x,y position.
		 *
		 */
		private function adjustCellRenderDisplayByScrollPosition():void
		{
			var currentProviderDataGroupIndex:int;
			var currentReuseCellRenderGroupIndex:int;
			var firstProviderDataGroupIndex:int = calculateStartProviderDataGroupIndexByScrollPosition(); //fist cellRender
			//scroll to increase cellIndex
			if (_scrollPosition > oldScrollPosition)
			{
				//reuse start index , form reuseIndex~ displayCount those cellRender can use in this render, others need to recycle
				//if firstVisableIndex = 10 , visableCellRenderVector[0].cellIndex = 7 the reUseIndex should start with 3
				//and if displayCount = 5(only show 5 cellrender) then should recycle 0,1,2 and contuine use 3,4,5
				currentReuseCellRenderGroupIndex = firstProviderDataGroupIndex - getCurrentFirstProviderDataGroupIndex();
				currentProviderDataGroupIndex = firstProviderDataGroupIndex;
				for (var cellRenderGroupIndex:int = 0; cellRenderGroupIndex <= displayGroupCount; cellRenderGroupIndex++)
				{
					//if preFirstScrollPosition is 0 , and current scrollPosition is 2 max displayCount is 4 (cellrender number is 5)
					//then reuse count start with 2
					//loop this function will swap 2 to 0 , 3 to 1 , 4 to 2
					//then reset data for 5 to 3 6 to 4
					if (currentReuseCellRenderGroupIndex <= displayGroupCount)
					{
						swapCellRenderGroup(cellRenderGroupIndex, currentReuseCellRenderGroupIndex);
					}
					else
					{
						refreshCellRenderGroup(cellRenderGroupIndex, currentProviderDataGroupIndex);
					}
					currentReuseCellRenderGroupIndex++;
					currentProviderDataGroupIndex++;
				}
			}
			//scroll to decrease cellIndex;
			else
			{
				// this time is difine the last index from reuseIndex~0 those cellRender can be use in this render ,others are need to recyle
				// also need to pay attention , this time is init from last cellRender
				currentReuseCellRenderGroupIndex = firstProviderDataGroupIndex - getCurrentFirstProviderDataGroupIndex() + displayGroupCount;
				currentProviderDataGroupIndex = firstProviderDataGroupIndex + displayGroupCount;
				for (var cellRenderGroupIndex2:int = displayGroupCount; cellRenderGroupIndex2 >= 0; cellRenderGroupIndex2--)
				{
					if (currentReuseCellRenderGroupIndex >= 0)
					{
						swapCellRenderGroup(cellRenderGroupIndex2, currentReuseCellRenderGroupIndex);
					}
					else
					{
						refreshCellRenderGroup(cellRenderGroupIndex2, currentProviderDataGroupIndex);
					}
					currentReuseCellRenderGroupIndex--;
					currentProviderDataGroupIndex--;
				}
			}
		}

		protected final function refreshAllCellRenderDispaly():void
		{
			var startCellDataIndex:int = calculateStartProviderDataGroupIndexByScrollPosition(); //fist cellRender
			for (var i:int = 0; i <= displayGroupCount; i++)
			{
				refreshCellRenderGroup(i, startCellDataIndex + i);
			}
			calculateMaxScrollPosition();
		}

		/**
		 * CEList will create (displayCount+1) cellRenders , this function will return the first
		 * one of them cellDataIndex.
		 */
		private function calculateStartProviderDataGroupIndexByScrollPosition():int
		{
			return Math.floor(_scrollPosition / (getCellRenderBoundSize() + groupContentPadding));
		}

		/**
		 *  	               ||00000-00000-00000-00000||-00000   ==> normal
		 * offset -> 000||00-00000-00000-00000-00||000        ==> offset is 000
		 *
		 * offset always >= 0
		 *
		 */
		protected final function getFirstCellRenderGroupOffset():int
		{
			return _scrollPosition % (getCellRenderBoundSize() + groupContentPadding);
		}

		/**
		 * with different layout will return different bound.
		 * LAYOUT_HORIZONTAL --> eachCellRenderWidth
		 * LAYOUT_VERTICAL -->       eachCellRenderHeight
		 */
		protected final function getCellRenderBoundSize():int
		{
			if (layoutDirection == LAYOUT_HORIZONTAL)
			{
				return eachCellRenderWidth;
			}
			else // layoutDirection == LAYOUT_VERTICAL
			{
				return eachCellRenderHeight
			}
		}

		private function calculateMaxScrollPosition():void
		{
			maxScrollPosition = Math.max(0, (getDataProviderGroupLength() - displayGroupCount) * (getCellRenderBoundSize() + groupContentPadding));
		}

		private function cleanEachCellRenderDataSelectableProperty(_cellRenderData:ICECellRenderData,
																   _index:int, _vector:Vector.<ICECellRenderData>):void
		{
			_cellRenderData.isSelected = false;
		}

		private function cleanEachCellRenderSelectableProperty(_cellRender:ICEOldCellRender,
															   _index:int, _vector:Vector.<ICEOldCellRender>):void
		{
			_cellRender.setIsSelected(false);
		}

		private function filterSelectableCellRenderData(_cellRenderData:ICECellRenderData,
														_index:int, _vector:Vector.<ICECellRenderData>):Boolean
		{
			return _cellRenderData.isSelected;
		}

		private function getCellRenderByCellIndex(_index:int):ICEOldCellRender
		{
			for each (var cellRender:ICEOldCellRender in cellRenderVector)
			{
				if (cellRender.cellIndex == _index)
				{
					return cellRender;
				}
			}
			return null;
		}

		//==================
		//==OverrideAble Function
		//==================
		protected function initListCoreScrollRect():void
		{
			throw new Error("Child Class should override this function");
		}

		/**
		 * based on current dataprovider , find the first dataprovider group index.
		 *
		 * WARNINIG::
		 * 			this function not base on current scrollPosition , beacuse when call this function scrollposition may already change
		 * and this function goal is to get the value before the scrollPosition change.
		 *
		 */
		protected function getCurrentFirstProviderDataGroupIndex():int
		{
			throw new Error("Child Class should override this function");
		}

		protected function getDataProviderGroupLength():int
		{
			throw new Error("Child Class should override this function");
		}

		protected function refreshCellRenderGroup(_cellRenderGroupIndex:int, _dataProviderGroupIndex:int):void
		{
			throw new Error("Child Class should override this function");
		}

		protected function swapCellRenderGroup(_groupIndex1:int, _groupIndex2:int):void
		{
			throw new Error("Child Class should override this function");
		}

		/**
		 * all cellRender number should be: displayCount + 1;
		 * based on current start CellRender Index to arrange those cellRender position.
		 */
		protected function layoutAllCellRender():void
		{
			throw new Error("Child Class should override this function");
		}

		protected function initializeAllCellRender(_cellRenderInstanceClass:Class, _cellRenderAnimationClass:Class):void
		{
			throw new Error("Child Class should override this function");
		}

		protected function disposeAllCellRender():void
		{
			throw new Error("Child Class should override this function");
		}

		protected function setDataProvider(_dataProvider:ICEListDataProvider):void
		{
			throw new Error("Child Class should override this function");
		}

		protected function disposeDataProvider():void
		{
			throw new Error("Child Class should override this function");
		}

	}
}
